home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / bin / dh_pycentral < prev    next >
Text File  |  2008-05-28  |  24KB  |  764 lines

  1. #!/usr/bin/perl -w
  2.  
  3. =head1 NAME
  4.  
  5. dh_pycentral - use the python-central framework to handle Python modules and extensions
  6.  
  7. =cut
  8.  
  9. use strict;
  10. use File::Find;
  11. use Debian::Debhelper::Dh_Lib;
  12.  
  13. =head1 SYNOPSIS
  14.  
  15. B<dh_pycentral> [S<I<debhelper options>>] [B<-n>] [B<-X>I<item>] [B<-V> I<version>] [S<I<module dirs ...>>]
  16.  
  17. =head1 DESCRIPTION
  18.  
  19. dh_pycentral is a debhelper program that will scan your package, detect
  20. public Python modules and move them in /usr/share/pycentral so that
  21. python-central can byte-compile those for all supported Python versions.
  22. Extensions are kept into the original installation location.
  23.  
  24. Moving the files to the pycentral location can be disabled by setting
  25. the environment varibale DH_PYCENTRAL to a string containing the
  26. string B<nomove>.
  27.  
  28. To shorten the time of unavailabilty of files during unpack and
  29. configure, dh_pycentral does the symlinking in the preinst and does
  30. not remove the symlinked files on upgrade. This can be disabled by
  31. setting the environment varibale DH_PYCENTRAL to a string containing
  32. the string B<noprepare>. If the newer version of a package needs to
  33. remove the symlinked files on upgrade, either the package needs to
  34. take care of the removal by calling B<pycentrel pkgremove> in the new
  35. preinst, or leaving a file /var/lib/pycentral/<package>.pkgremove and
  36. using pycentral 0.6.7 or later for the old package version.
  37.  
  38. You must have filled the XS-Python-Version header to indicate the
  39. set of python versions that are going to be supported. dh_pycentral
  40. expects the XB-Python-Version for each binary package it is supposed
  41. to work on.
  42.  
  43. dh_pycentral will also generate substitution variables: the
  44. ${python:Provides} variable will contain versioned provides of the package
  45. (if the package's name starts with "python-"). A python-foo package could
  46. provide "python2.3-foo" and "python2.4-foo" at the same time. Python
  47. extensions have to provide those whereas it's only option for pure python
  48. modules.
  49.  
  50. The ${python:Versions} variable should be used to provide the required
  51. XB-Python-Version field listing the python versions supported by the
  52. package.
  53.  
  54. =head1 OPTIONS
  55.  
  56. =over 4
  57.  
  58. =item I<module dirs>
  59.  
  60. If your package installs python modules in non-standard directories, you
  61. can make dh_pycentral check those directories by passing their names on the
  62. command line. By default, it will check /usr/lib/$PACKAGE, /usr/share/$PACKAGE, /usr/lib/games/$PACKAGE,
  63. /usr/share/games/$PACKAGE and /usr/lib/python?.?/site-packages.
  64.  
  65. Note: only /usr/lib/python?.?/site-packages and the
  66. extra names on the command line are searched for binary (.so) modules.
  67.  
  68. =item B<-V> I<version>
  69.  
  70. If the .py files your package ships are meant to be used by a specific
  71. pythonX.Y version, you can use this option to specify the desired version,
  72. such as 2.3. Do not use if you ship modules in /usr/lib/site-python.
  73.  
  74. With the new policy, this option is mostly deprecated. Use the
  75. XS-Python-Field to indicate that you're using a specific python version.
  76.  
  77. =item B<-n>, B<--noscripts>
  78.  
  79. Do not modify postinst/postrm scripts.
  80.  
  81. =item B<-X>I<item>, B<--exclude=>I<item>
  82.  
  83. Exclude files that contain "item" anywhere in their filename from being
  84. taken into account to generate the python dependency. You may use this
  85. option multiple times to build up a list of things to exclude.
  86.  
  87. =back
  88.  
  89. =head1 CONFORMS TO
  90.  
  91. Python policy, version 0.4.1 (2006-06-20)
  92.  
  93. =cut
  94.  
  95. init();
  96.  
  97. # required pycentral version
  98. my $pycentral_version = '0.6.7';
  99.  
  100. # format version used for the preinst script
  101. my $pyformat = '1';
  102.  
  103. my $python = 'python';
  104.  
  105. # The current python major version
  106. my $python_major;
  107. my $python_version = `$python -V 2>&1`;
  108. if (! defined $python_version || $python_version eq "") {
  109.     error("Python is not installed, aborting. (Probably forgot to Build-Depend on python.)");
  110. }
  111. elsif ($python_version =~ m/^Python\s+(\d+)\.(\d+)(\.\d+)*/) {
  112.     $python_version = "$1.$2" ;
  113.     $python_major = $1 ;
  114. } else { 
  115.     error("Unable to parse python version out of \"$python_version\".");
  116. }
  117.  
  118. # The next python version
  119. my $python_nextversion = next_minor_version($python_version);
  120. my $python_nextmajor = $python_major + 1;
  121.  
  122. my @python_allversions = ('1.5','2.1','2.2','2.3','2.4','2.5','2.6');
  123. foreach (@python_allversions) {
  124.     s/^/python/;
  125. }
  126.  
  127. # Check for -V
  128. my $usepython = "python$python_version";
  129. if($dh{V_FLAG_SET}) {
  130.     $usepython = $dh{V_FLAG};
  131.     $usepython =~ s/^/python/;
  132.     if (! grep { $_ eq $usepython } @python_allversions) {
  133.         error("Unknown python version $dh{V_FLAG}");
  134.     }
  135. }
  136.  
  137. # Cleaning the paths given on the command line
  138. foreach (@ARGV) {
  139.     s#/$##;
  140.     s#^/##;
  141. }
  142.  
  143. # Check the compatibilty mode to use
  144. my $pyversions_field = "";
  145. my $python_header = "";
  146. {
  147.     local $/ = ""; # Grab until empty line
  148.     open(CONTROL, "debian/control"); # Can't fail, dh_testdir has already been called
  149.     my $source = <CONTROL>;
  150.     close(CONTROL);
  151.     if ($source =~ m/^XS-Python-Version: \s*(.*)$/m) {
  152.         $python_header = $1;
  153.         chomp($python_header);
  154.         $pyversions_field = convert_python_header($python_header);
  155.     }
  156. }
  157.  
  158. # pyversions describes the list of python versions that this package can
  159. # work with
  160. if (-e "debian/pyversions") {
  161.     open(PYVERS, "debian/pyversions") || error("Can't open debian/pyversions: $!");
  162.     $pyversions_field = <PYVERS>;
  163.     chomp($pyversions_field);
  164.     close(PYVERS);
  165. }
  166. verbose_print("Pyversions field: $pyversions_field");
  167.  
  168. # Extract a list of officially supported Python versions
  169. my %officially_supported;
  170. if (-e "/usr/share/python/debian_defaults") {
  171.     open(DEFAULTS, "/usr/share/python/debian_defaults") ||
  172.         error("Can't open /usr/share/python/debian_defaults: $!");
  173.     foreach (<DEFAULTS>) {
  174.         if (/^supported-versions\s*=\s*(.*)$/) {
  175.             my $supported = $1;
  176.             foreach my $version (split(/\s+/, $supported)) {
  177.                 if ($version =~ /python([\d\.]+)/) {
  178.                     $officially_supported{$1} = 1;
  179.                 }
  180.             }
  181.             last;
  182.         }
  183.     }
  184.     close(DEFAULTS);
  185. }
  186.  
  187. # dependency types
  188. use constant PROGRAM   => 1;
  189. use constant PY_PRIVATE_MODULE => 2;
  190. use constant PY_PUBLIC_MODULE => 4;
  191. use constant PY_OFFICIAL_MODULE => 8;
  192. use constant PY_UNKNOWN_MODULE => 16;
  193. use constant SO_PRIVATE_MODULE => 32;
  194. use constant SO_PUBLIC_MODULE => 64;
  195. use constant SO_OFFICIAL_MODULE => 128;
  196. use constant SO_UNKNOWN_MODULE => 256;
  197.  
  198. foreach my $package (@{$dh{DOPACKAGES}}) {
  199.     my $tmp = tmpdir($package);
  200.  
  201.     # Move *.py files if needed
  202.     doit("pycentral debhelper $package $tmp");
  203.  
  204.     # Check that we have *.py files!
  205.     my $found = 0;
  206.     find sub {
  207.         return if $File::Find::dir =~ m|^\Q$tmp\E/usr/share/doc/|;
  208.         return unless -f and /\.py$/;
  209.         $found++;
  210.     }, $tmp;
  211.  
  212.     # Here we're doing what dh_python used to do
  213.     my @dirs = ("usr/lib/$package", "usr/share/$package", "usr/lib/games/$package", "usr/share/games/$package", @ARGV );
  214.     my @dirs_so = (@ARGV);
  215.  
  216.     my $dep_on_python = 0;
  217.     my $strong_dep = 0;
  218.  
  219.     # Fail early if the package use usr/lib/site-python
  220.     if (-d "$tmp/usr/lib/site-python") {
  221.         error("The package $package puts files in /usr/lib/site-python: forbidden by policy");
  222.     }
  223.  
  224.     @dirs = grep -d, map "$tmp/$_", @dirs;
  225.     @dirs_so = grep -d, map "$tmp/$_", @dirs_so;
  226.  
  227.     my $deps = 0;
  228.     my %verdeps = ();
  229.     foreach (@python_allversions) {
  230.         $verdeps{$_} = 0;
  231.     }
  232.  
  233.     # Global scan
  234.     my $private_pydirs_regex = join('|', map { "\Q$_\E" } @dirs);
  235.     my $private_sodirs_regex = join('|', map { "\Q$_\E" } @dirs_so);
  236.     my %private_dirs_list;
  237.     my %pyversions_found;
  238.     find sub {
  239.         return unless -f;
  240.         # See if we were asked to exclude this file.
  241.         # Note that we have to test on the full filename, including directory.
  242.         my $fn="$File::Find::dir/$_";
  243.         if (excludefile($fn)) {
  244.             verbose_print("Ignoring $fn");
  245.             return;
  246.         }
  247.         # Find scripts
  248.         if (-x or /\.py$/) {
  249.             local *F;
  250.             return unless open F, $_;
  251.             if (read F, local $_, 32 and m%^#!\s*/usr/bin/(env\s+)?(python(\d+\.\d+)?)\s%) {
  252.                 verbose_print("Found program using $2: $fn");
  253.                 if ( "python" eq $2 ) {
  254.                     $deps |= PROGRAM;
  255.                 } elsif(defined $verdeps{$2}) {
  256.                     $verdeps{$2} |= PROGRAM;
  257.                 }
  258.             }
  259.             close F;
  260.         }
  261.         # Continue only with .py or .so
  262.         return unless (/\.py$/ or /\.so$/);
  263.  
  264.         # Remove any byte-compiled file
  265.         doit(("rm","-f",$_."c",$_."o")) if /\.py$/;
  266.         
  267.         # Classify the file in the right category
  268.         if (/\.py$/ and $private_pydirs_regex and $fn =~ m/(?:$private_pydirs_regex)/) {
  269.             # Private python module
  270.             verbose_print("Found private module: $fn");
  271.             my $dir;
  272.             foreach $dir (@dirs) {
  273.                 if ($fn =~ m/\Q$dir\E/) {
  274.                     $dir =~ s/^$tmp//;
  275.                     verbose_print("Memorizing dir to byte-compile: $dir");
  276.                     $private_dirs_list{"$dir"} = 1;
  277.                 }
  278.             }
  279.             if ($dh{V_FLAG_SET}) {
  280.                 $verdeps{$usepython} |= PY_PRIVATE_MODULE;
  281.             } else {
  282.                 $deps |= PY_PRIVATE_MODULE;
  283.             }
  284.         } elsif (/\.so$/ and $private_sodirs_regex and $fn =~ m/(?:$private_sodirs_regex)/) {
  285.             # Private python extension
  286.             verbose_print("Found private extension: $fn");
  287.             if ($dh{V_FLAG_SET}) {
  288.                 $verdeps{$usepython} |= SO_PRIVATE_MODULE;
  289.             } else {
  290.                 $deps |= SO_PRIVATE_MODULE;
  291.             }
  292.         } elsif ($fn =~ m|$tmp/usr/lib/python([\d\.]+)/site-packages/|) {
  293.             $pyversions_found{$1} = 1;
  294.             my $v = $1;
  295.             if (/\.py$/) {
  296.                 verbose_print("Found public module: $fn");
  297.                 $verdeps{"python$v"} |= PY_PUBLIC_MODULE;
  298.             } else {
  299.                 verbose_print("Found public extension: $fn");
  300.                 $verdeps{"python$v"} |= SO_PUBLIC_MODULE;
  301.             }
  302.         } elsif ($fn =~ m|$tmp/usr/lib/python([\d\.]+)/|) {
  303.             $pyversions_found{$1} = 1;
  304.             my $v = $1;
  305.             if (/\.py$/) {
  306.                 verbose_print("Found official module: $fn");
  307.                 $verdeps{"python$v"} |= PY_OFFICIAL_MODULE;
  308.             } else {
  309.                 verbose_print("Found official extension: $fn");
  310.                 $verdeps{"python$v"} |= SO_OFFICIAL_MODULE;
  311.             }
  312.         } elsif ($fn =~ m|$tmp/usr/lib/python-support/[^/]+/python([\d\.]+)/|) {
  313.             $pyversions_found{$1} = 1;
  314.             my $v = $1;
  315.             if (/\.py$/) {
  316.                 verbose_print("Found public module: $fn");
  317.                 $verdeps{"python$v"} |= PY_PUBLIC_MODULE;
  318.             } else {
  319.                 verbose_print("Found public extension: $fn");
  320.                 $verdeps{"python$v"} |= SO_PUBLIC_MODULE;
  321.             }
  322.         } elsif ($fn =~ m{$tmp(?:/usr/share/pyshared/|/usr/share/python-support/)}) {
  323.             if (/\.py$/) {
  324.                 verbose_print("Found public module: $fn");
  325.                 $deps |= PY_PUBLIC_MODULE;
  326.             } # No extensions here
  327.         } elsif ($fn =~ m|$tmp/usr/lib/python([\d\.]+)/site-packages/|) {
  328.             
  329.         } elsif ($fn =~ m|$tmp/usr/share/doc/|) {
  330.             # Ignore .py files in doc directory
  331.         } else {
  332.             # Unknown pyfiles
  333.             if (/\.py$/) {
  334.                 verbose_print("Found unclassified module: $fn");
  335.                 if ($dh{V_FLAG_SET}) {
  336.                     $verdeps{$usepython} |= PY_UNKNOWN_MODULE;
  337.                 } else {
  338.                     $deps |= PY_UNKNOWN_MODULE;
  339.                 }
  340.             } else {
  341.                 verbose_print("Found unclassified extension: $fn");
  342.                 if ($dh{V_FLAG_SET}) {
  343.                     $verdeps{$usepython} |= SO_UNKNOWN_MODULE;
  344.                 } else {
  345.                     $deps |= SO_UNKNOWN_MODULE;
  346.                 }
  347.             }
  348.         }
  349.     }, $tmp;
  350.  
  351.     #
  352.     # NEW POLICY
  353.     #
  354.     # Generate the depends to accept all python
  355.     # versions that this package effectively provides
  356.     my ($min_version, $max_version, @versions) = analyze_pyversions($pyversions_field);
  357.     my $stop_version = "";
  358.     $stop_version = next_minor_version($max_version) if $max_version;
  359.     my $versions_field = "";
  360.     verbose_print("Pyversions analysis gives: min=$min_version, max=$max_version (@versions)");
  361.  
  362.     # Common dependency handling
  363.     foreach my $pyver (keys %verdeps) {
  364.         # Always add pythonX.Y dependency if a script uses that interpreter
  365.         if ($verdeps{$pyver} & PROGRAM) {
  366.             addsubstvar($package, "python:Depends", $pyver);
  367.         }
  368.         # Always add pythonX.Y dependency if some private modules are
  369.         # byte-compiled with it (or if extensions are
  370.         # byte-compiled with it)
  371.         if ($verdeps{$pyver} & (PY_PRIVATE_MODULE|SO_PRIVATE_MODULE)) {
  372.             addsubstvar($package, "python:Depends", $pyver);
  373.             unless ($versions_field) {
  374.                 $versions_field = $pyver;
  375.                 $versions_field =~ s/^python//;
  376.             }
  377.         }
  378.     }
  379.  
  380.     # Reset again, analysis using new policy follows
  381.     $dep_on_python = 0;
  382.  
  383.     # Private extensions, must be rebuilt for each python version
  384.     if ($deps & SO_PRIVATE_MODULE) {
  385.         if (($dh{V_FLAG_SET} and ($usepython eq "python$python_version")) or
  386.             (($min_version eq $python_version) and ($min_version eq $max_version))
  387.            ) {
  388.             # Depend on python only if the version
  389.             # used to build is the currently supported one
  390.             $dep_on_python++;
  391.         }
  392.         # Packages using a private extension can only support one
  393.         # version: if versions_field is already set that's because
  394.         # we're using -V and we should respect that, otherwise try
  395.         # the best guess:
  396.         unless($versions_field) {
  397.             if ($min_version and ($min_version eq $max_version)) {
  398.                 # Only one version supported, use it
  399.                 $versions_field = $min_version;
  400.             } elsif (compare_version($min_version, $python_version) > 0) {
  401.                 # The current version is unsupported, use the min version instead
  402.                 $versions_field = $min_version;
  403.             } else {
  404.                 # Use the current version by default
  405.                 $versions_field = $python_version;
  406.                 $min_version = $python_version;
  407.             }
  408.         }
  409.         $stop_version = next_minor_version($versions_field);
  410.     }
  411.  
  412.     # Private modules 
  413.     if ($deps & PY_PRIVATE_MODULE) {
  414.         # Package with private modules can only support one version at a time
  415.         # (even if the modules can be automatically byte-compiled for any new
  416.         # python version).
  417.         unless ($versions_field) {
  418.             # Unless min/max are the same we put "current"
  419.             if ($min_version and ($min_version eq $max_version)) {
  420.                 $versions_field = $min_version;
  421.             } elsif (compare_version($min_version, $python_version) > 0) {
  422.                 # The current version is unsupported, use the min version instead
  423.                 $versions_field = $min_version;
  424.             } else {
  425.                 # Use the current version by default
  426.                 $versions_field = "current";
  427.             }
  428.         }
  429.     }
  430.  
  431.     # Python scripts & public modules
  432.     if ($deps & (PROGRAM|PY_PUBLIC_MODULE)) {
  433.         $dep_on_python++;
  434.     }
  435.     
  436.     # Public extensions
  437.     if (scalar keys %pyversions_found) {
  438.         # Extensions will always trigger this (as well as public
  439.         # modules not handled by python-support/python-central)
  440.         $dep_on_python++;
  441.         if (scalar grep { $python_version eq $_ } keys %pyversions_found) {
  442.             # Current versions is supported by the package
  443.             # It's safe to depends on the corresponding
  444.             # interval of python versions
  445.             $min_version = min(keys %pyversions_found);
  446.             unless ($stop_version) {
  447.                 $max_version = max(keys %pyversions_found);
  448.                 $stop_version = next_minor_version($max_version);
  449.             }
  450.         } else {
  451.             # Current version is not supported by the package
  452.             if ($min_version and ($min_version eq $max_version)) {
  453.                 # If we support only one non-standard python
  454.                 # version, the depend on that version and not on python
  455.                 $dep_on_python--;
  456.                 if (! $dep_on_python) {
  457.                     addsubstvar($package, "python:Depends", "python$min_version");
  458.                 }
  459.             }
  460.         }
  461.         # Generate the Python-Version field
  462.         foreach (keys %pyversions_found) {
  463.             addsubstvar($package, "python:Versions", $_);
  464.         }
  465.         $versions_field = join(", ", keys %pyversions_found);
  466.         # Generate provides for the python2.X-foo packages that we emulate
  467.         if ($package =~ /^python-/) {
  468.             foreach (keys %pyversions_found) {
  469.                 my $virtual = $package;
  470.                 $virtual =~ s/^python-/$python$_-/;
  471.                 addsubstvar($package, "python:Provides", $virtual);
  472.             }
  473.         }
  474.     } else {
  475.         # Still try to describe the versions that the package support
  476.         $versions_field = $python_header if ($versions_field eq "current");
  477.         $versions_field = $python_header unless $versions_field;
  478.         $versions_field = "all" unless $versions_field;
  479.         addsubstvar($package, "python:Versions", $versions_field);
  480.  
  481.         # Generate provides for all python versions supported
  482.         if ($package =~ /^python-/) {
  483.             foreach (grep { $officially_supported{$_} } @versions) {
  484.                 my $virtual = $package;
  485.                 $virtual =~ s/^python-/$python$_-/;
  486.                 addsubstvar($package, "python:Provides", $virtual);
  487.             }
  488.         }
  489.     }
  490.     
  491.     if ($dep_on_python) {
  492.         # At least a script has been detected
  493.         if ($min_version) {
  494.             if (compare_version($min_version, $python_version) <= 0 ) {
  495.                 # Min-version is less or equal to current version
  496.                 addsubstvar($package, "python:Depends", $python, ">= $min_version");
  497.             } else {
  498.                 # Min version is greater to current version
  499.                 # Supposition: new stuff working only with the latest python, 
  500.                 # depend on that specific python version
  501.                 addsubstvar($package, "python:Depends", 
  502.                         "python (>= $min_version) | python$min_version");
  503.             }
  504.         }
  505.         # If a stronger dependency is needed
  506.         if ($stop_version) {
  507.             if (compare_version($stop_version, $python_version) > 0 ) {
  508.                 # Stop version is strictly bigger than current version
  509.                 addsubstvar($package, "python:Depends", $python, "<< $stop_version");
  510.             } else {
  511.                 # Only works with old versions,
  512.                 # package is mostly deprecated,
  513.                 # no need for a python dependency
  514.             }
  515.         }
  516.         # Let's depend on python anyway
  517.         addsubstvar($package, "python:Depends", $python) unless ($min_version or $stop_version);
  518.     }
  519.  
  520.     if (-d "$tmp/usr/share/pycentral") {
  521.         error("The package $package puts files in /usr/share/pycentral instead of /usr/share/pyshared; use `pycentral pycentraldir' to determine the installation directory");
  522.  
  523.     }
  524.  
  525.     if ($found or -d "$tmp/usr/share/pyshared") {
  526.         addsubstvar($package, "python:Depends", "python-central", ">= $pycentral_version");
  527.         # FIXME: move back, after $versions_field is set.
  528.         if (! $dh{NOSCRIPTS}) {
  529.             my $outfile = "debian/".pkgext($package)."preinst.debhelper";
  530.             my $savedfile = "$outfile.saved";
  531.             my $prerm_upgrade = "|upgrade";
  532.             if (-f $outfile) {
  533.                 doit(("cp","-p",$outfile,$savedfile));
  534.             } else {
  535.                 $savedfile = "";
  536.             }
  537.             autoscript($package,"preinst","preinst-pycentral","s%#PACKAGE#%$package%;s%#FORMAT#%$pyformat%;s%#PYVERSIONS#%$versions_field%;s%#PYCENTRAL_VERSION#%$pycentral_version%");
  538.             complex_doit("find debian/$package -mindepth 1 -regex '.*/usr/share/doc/.*' -prune -o \\( -regex '.*/usr/share/pyshared/.*' -o -regex '.*/usr/lib/python2\.[3-9]/site-packages/.*' -o -name '*.py' \\) -printf '/%P=%Y\n' > $outfile.pyc");
  539.             complex_doit("perl -pi -e '/#PYFILES#/ and do { open F, \"$outfile.pyc\"; local \$/ ; \$_ = <F> };' $outfile");
  540.             doit(("mkdir","-p","-m","755","debian/$package/usr/share/pyshared-data"));
  541.             complex_doit("awk '/^\\[python-package\\]\$/, /^PYEOF\$/ { if (\$1 ~ /PYEOF/) exit; else print}' $outfile > debian/$package/usr/share/pyshared-data/$package");
  542.             doit(("chmod","644","debian/$package/usr/share/pyshared-data/$package"));
  543.             doit(("rm","-f","$outfile.pyc"));
  544.             if (!(-d "$tmp/usr/share/pyshared" || ($deps & PY_PRIVATE_MODULE)) || ($ENV{DH_PYCENTRAL} && $ENV{DH_PYCENTRAL} =~ /noprepare/)) {
  545.                 if ($savedfile eq "") {
  546.                     doit(("rm","-f","$outfile"));
  547.                 } else {
  548.                     doit(("cp","-p","$outfile.saved",$outfile));
  549.                 }
  550.             } else {
  551.                 if (!($deps & PY_PRIVATE_MODULE)) {
  552.                     $prerm_upgrade = "";
  553.                 }
  554.             }
  555.             doit(("rm","-f","$savedfile"));
  556.             autoscript($package,"postinst","postinst-pycentral","s%#PACKAGE#%$package%");
  557.             autoscript($package,"prerm","prerm-pycentral","s%#PACKAGE#%$package%;s%#UPGRADE#%$prerm_upgrade%");
  558.         }
  559.     }
  560. }
  561.  
  562. sub next_minor_version {
  563.     my $version = shift;
  564.     # Handles 2.10 -> 2.11 gracefully
  565.     my @items = split(/\./, $version);
  566.     $items[1] += 1;
  567.     $version = join(".", @items);
  568.     return $version;
  569. }
  570.  
  571. sub compare_version {
  572.     my ($a, $b) = @_;
  573.     my @A = split(/\./, $a);
  574.     my @B = split(/\./, $b);
  575.     my $diff = 0;
  576.     for (my $i = 0; $i <= $#A; $i++) {
  577.     $diff = $A[$i] - $B[$i];
  578.     return $diff if $diff; 
  579.     }
  580.     # They are the same
  581.     return 0;
  582. }
  583.  
  584. sub max {
  585.     my $max = shift;
  586.     foreach (@_) {
  587.     $max = $_ if (compare_version($_, $max) > 0);
  588.     }
  589.     return $max;
  590. }
  591.  
  592. sub min {
  593.     my $min = shift;
  594.     foreach (@_) {
  595.     $min = $_ if (compare_version($_, $min) < 0);
  596.     }
  597.     return $min;
  598. }
  599.  
  600. # Extract min, max and list of versions from
  601. # a string like this "-1.2,1.4-1.6,2.0,2.3-"
  602. sub analyze_pyversions {
  603.     my $field = shift;
  604.     my ($min, $max, @versions);
  605.  
  606.     my @all_versions = ("0.0");
  607.     foreach (@python_allversions) {
  608.     if (m/python([\d\.]+)/) {
  609.         push @all_versions, $1;
  610.     }
  611.     }
  612.     push @all_versions, "100.0";
  613.  
  614.     foreach my $range (split /,/, $field) {
  615.     if ($range =~ /^([\d\.]+)?-([\d\.]+)?$/) {
  616.         $min = defined($1) && $1 ? $1 : "0.0";
  617.         $max = defined($2) && $2 ? $2 : "100.0";
  618.         push @versions, grep { 
  619.         compare_version($_, $min) >= 0 and 
  620.         compare_version($_, $max) <= 0 } @all_versions;
  621.     } else {
  622.         push @versions, $range if (grep { $range eq $_ } @all_versions);
  623.     }
  624.     }
  625.  
  626.     $min = min(@versions);
  627.     $max = max(@versions);
  628.     $min = "" if (!defined($min) or $min eq "0.0");
  629.     $max = "" if (!defined($max) or $max eq "100.0");
  630.  
  631.     @versions = grep { $_ ne "0.0" and $_ ne "100.0" } @versions;
  632.  
  633.     return ($min, $max, @versions);
  634. }
  635.  
  636. # Convert the Python-Version field in a standardized "pyversions" field
  637. sub convert_python_header {
  638.     my $header = shift;
  639.     my %pyversions_expected;
  640.     my %pyversions_additional;
  641.     my $use_additional = 0;
  642.     my @all_versions = ();
  643.     foreach (@python_allversions) {
  644.     if (m/python([\d\.]+)/) {
  645.         $pyversions_additional{$1} = 1;
  646.         push @all_versions, $1;
  647.     }
  648.     }
  649.     # Add two fakes minima, maxima to check if we have limits
  650.     $pyversions_additional{"0.0"} = 1;
  651.     $pyversions_additional{"100.0"} = 1;
  652.  
  653.     # Compute the list of versions that are supported
  654.     foreach my $check (split(/,\s*/, $header)) {
  655.     if ($check =~ /^=?\s*([\d\.]+)/) {
  656.         # Add any fixed version
  657.         $pyversions_expected{$1} = 1 if (grep { $_ eq $1 } @all_versions);
  658.         next;
  659.     }
  660.     # Deactivate versions which do not match the other
  661.     # criteria
  662.     if ($check =~ /^<<\s*([\d\.]+)/) {
  663.         my $v = $1;
  664.         $use_additional = 1;
  665.         foreach (keys %pyversions_additional) {
  666.         $pyversions_additional{$_} = 0 unless (compare_version($_, $v) < 0);
  667.         }
  668.     } elsif ($check =~ /^<=\s*([\d\.]+)/) {
  669.         my $v = $1;
  670.         $use_additional = 1;
  671.         foreach (keys %pyversions_additional) {
  672.         $pyversions_additional{$_} = 0 unless (compare_version($_, $v) <= 0);
  673.         }
  674.     } elsif ($check =~ /^>=\s*([\d\.]+)/) {
  675.         my $v = $1;
  676.         $use_additional = 1;
  677.         foreach (keys %pyversions_additional) {
  678.         $pyversions_additional{$_} = 0 unless (compare_version($_, $v) >= 0);
  679.         }
  680.     }
  681.     }
  682.     if ($use_additional) {
  683.     foreach (grep { $pyversions_additional{$_} } keys %pyversions_additional) {
  684.         $pyversions_expected{$_} = 1;
  685.     }
  686.     }
  687.  
  688.     my @supported = sort { compare_version($a, $b) } 
  689.             grep { $pyversions_expected{$_} == 1 } keys %pyversions_expected;
  690.     
  691.     verbose_print("List of versions supported according to XS-Python-Version: @supported");
  692.  
  693.     # Generate the corresponding information in standardized "pyversions" format
  694.     # XXX: I go to great length to generate the shortest pyversions
  695.     # syntax but it may not be necessary for the usage that we make of it.
  696.     my ($result, $index, $range_is_open, $last_real_version) = ("", 0, 0, "");
  697.     foreach (@supported) {
  698.     if ($_ eq "0.0") {
  699.         $result .= "-"; # No lower limit
  700.         $range_is_open = 1;
  701.     } elsif ($_ eq "100.0") {
  702.         $result .= "-" unless $range_is_open; # No upper limit
  703.         $range_is_open = 0; # Proper end
  704.     } else {
  705.         $last_real_version = $_;
  706.         if ($range_is_open) {
  707.         # Range is open
  708.         if ($index <= $#all_versions and $all_versions[$index] eq $_) {
  709.             # This version still in the range
  710.         } else {
  711.             # Previous version ended the range
  712.             $result .= $all_versions[$index-1] . ",$_";
  713.             $range_is_open = 0;
  714.         }
  715.         # Find the current version in all_versions
  716.         while ($index <= $#all_versions) {
  717.             last if ($all_versions[$index] eq $_);
  718.             $index++;
  719.         }
  720.         } else {
  721.         # There's no range yet
  722.         if ($result) {
  723.             if ($index <= $#all_versions and $all_versions[$index] eq $_) {
  724.             # This version still match, we can start a range
  725.             $result .= "-";
  726.             $range_is_open = 1;
  727.             } else {
  728.             # Next version doesn't match, no range but a list
  729.             $result .= ",$_";
  730.             }
  731.         } else {
  732.             # Empty field, start with something!
  733.             $result = "$_";
  734.         }
  735.         while ($index <= $#all_versions) {
  736.             last if ($all_versions[$index] eq $_);
  737.             $index++;
  738.         }
  739.         }
  740.         $index++;
  741.     }
  742.     }
  743.     if ($range_is_open) {
  744.     # Close the range properly
  745.     $result .= $last_real_version;
  746.     }
  747.     return $result;
  748. }
  749.  
  750. =head1 SEE ALSO
  751.  
  752. L<debhelper(7)>
  753.  
  754. This program is a part of python-central but is made to work with debhelper.
  755.  
  756. =head1 AUTHORS
  757.  
  758. Raphael Hertzog <hertzog@debian.org>
  759.  
  760. Also includes bits of the old dh_python written by Josselin Mouette
  761. <joss@debian.org> who used many ideas from Brendan O'Dea <bod@debian.org>.
  762.  
  763. =cut
  764.